/***************************************************************************//**
 * @file mac_TDMA.c
 * @brief for mac TDMA
 * @author ze .
 * @date  2022/7/03.
 * @copyright Copyright 
 ******************************************************************************/
#include"mac_TDMA.h"
#include "mac_transmitter.h"
#include "mac_timeSync.h"
#include "softwareMultiTimer.h"
#include "mac_packetHandle.h"
#include "device.h"

/**************************************************HUB使用，宏定义及变量声明 默认全局心跳时间**************************************************************/
#define SPVN_SLOT_SUB_GROUP_LENGTH    10 //SPVN_SLOT_SUB_GROUP_NUMBER * SPVN_SLOT_SUB_GROUP_LENGTH = SPVN_SLOT_NUM
#define SPVN_SLOT_SUB_GROUP_NUMBER   (SPVN_SLOT_NUM / 10)

SPVN_SLOT_MAP_GROUP_ST pstSpvnSlotMapGroup[SPVN_SLOT_GROUP_NUM];//Default 默认全局心跳的分配槽

FILE_SLOT_MAP_GROUP_ST pstFileSlotMapGroup[FILE_SLOT_GROUP_NUM];
/**************************************************RFM使用，宏定义及变量声明**************************************************************/
static SPVN_SLOT_CB_ST s_spvnSlot ={0xFF,0,0,0};

uint8_t getNodeId() //获取nodeId，根据该Id计算时间槽
{
  uint8_t nodeId = 0;//
  getThisParam(ENpbyRfmId, &nodeId); // 获取nodeId，
  if(nodeId == 0)
  {
      nodeId = (uint8_t)READ_SYS_RTC_COUNT();//若未注册，使用随机值
  }
  return nodeId;
}
//心跳槽初始化
void spvnSlotInit()
{
  uint8_t nodeId = getNodeId();
  if(SPVN_TIME_1MIN == getDeviceSpvnTime()){
    setSpvnSlot(4 + (nodeId / 32) , nodeId % 32 , 64);//2个心跳丢失的时间128s ，判断离线的时间为128+12=140s？
  }
  else if(SPVN_TIME_30S == getDeviceSpvnTime()){
      setSpvnSlot(10 + (nodeId / 16) , nodeId % 16 , 32);
  }
  else
    setSpvnSlot( nodeId / SPVN_SLOT_NUM , nodeId % SPVN_SLOT_NUM , SPVN_TIME_DEFAULT);
}

void setSpvnSlot(uint8_t groupIdx, uint8_t slotIdx, uint16_t spvnTime)//SpvnTime 单位s
{
  uint32_t temp32 = 0;

  s_spvnSlot.byGroupIdx = groupIdx + 1;//add 231110，避免使用H1，从H2开始使用，后续若网关分配也应如此。
  s_spvnSlot.bySlotIdx = slotIdx;
  s_spvnSlot.wSpvnTime = spvnTime;

  if(s_spvnSlot.byGroupIdx < SPVN_SLOT_GROUP_MAX_NUM)
  {
      temp32 = s_spvnSlot.byGroupIdx;//组别
  }
  else
  {
      temp32 = (uint8_t)READ_SYS_RTC_COUNT() % SPVN_SLOT_GROUP_MAX_NUM;//随机数
  }
  temp32 = ( 13927 + (SPVN_WINDOW_WIDTH*(temp32/4)) ) + ( 13107*(temp32%4) );//根据TDMA分配，计算2s槽内的位置
  s_spvnSlot.dwDeadline = (((uint32_t)(s_spvnSlot.bySlotIdx % (spvnTime/2))) << 16) + temp32;// 一般bySlotIdx <= spvnTime/2，此处可防止slotIdx大于spvnTime/2导致的心跳周期变大问题 //根据心跳周期，计算所属2s槽在心跳周期内的位置
  printf("ST%d DL%d\r\n",s_spvnSlot.wSpvnTime,s_spvnSlot.dwDeadline);
}


void getNextSpvnDeadline(uint16_t* pOutSpvnTime ,uint32_t* pOutNextDeadline)
{
  if(s_spvnSlot.byGroupIdx == 0xFF) //若开启心跳定时器时获取时间 未被初始化，则根据nodeId重新初始化
  {
      spvnSlotInit();//心跳槽初始化
  }
  //获取心跳间隔以及 deadline
  *pOutSpvnTime = s_spvnSlot.wSpvnTime;
  *pOutNextDeadline = s_spvnSlot.dwDeadline;
}

/**************************************************HUB使用，函数定义**************************************************************/
/***
 *@brief 全清0初始化心跳时间槽 
 *@param 
 *@param
 *@return int >0 maclloced idx； -1 failed
 * 
*/
int spvnSlotClearInit(void) 
{
    uint8_t i,j;

    for(j=0;j<SPVN_SLOT_GROUP_NUM;j++)
    {
        pstSpvnSlotMapGroup[j].bySeq = 0;
        //遍历整个槽组
        for(i=0;i<SPVN_SLOT_NUM;i++)
        {
            pstSpvnSlotMapGroup[j].byMallocedId[i] =  0; //clear the malloced id.
        } 
    } 

}   

void spvnSlotPrintfAll(void) 
{
    uint8_t i,j;

    for(j=0;j<SPVN_SLOT_GROUP_NUM;j++)
    {
        printf("SGP%d:\r\n",j);
        //遍历整个槽组
        for(i=0;i<SPVN_SLOT_NUM;i++)
        {
            printf("/%d",pstSpvnSlotMapGroup[j].byMallocedId[i]); //clear the malloced id.
        } 
        printf("\r\n");
    } 

}   

/***
 *@brief 分配心跳时间槽 ，共SPVN_SLOT_NUM个槽 每个间隔2s，又分成SPVN_SLOT_SUB_GROUP_NUMBER个子组，每子组SPVN_SLOT_SUB_GROUP_LENGTH10个槽长度即20s；
 *@param nodeId，提出申请的nodeId
 *@param SpvnTime， SpvnTime心跳周期 单位s
 *@return int >0 maclloced idx； -1 failed
 * 
*/
int spvnSlotMalloc(uint8_t nodeId, uint16_t SpvnTime) 
{
    int ret = -1;
    uint8_t temp8 = 0;
    uint8_t i,j;                                   //0,1,2,3,4,5,6,7,8,9
    const uint8_t map[SPVN_SLOT_SUB_GROUP_LENGTH] = {0,5,3,7,1,8,2,6,4,9};
    int seq = 0;
    if(SPVN_TIME_DEFAULT == SpvnTime)
    {
        for(j=0;j<SPVN_SLOT_GROUP_NUM;j++)
        {
            //Group1 优先分配给默认心跳周期的节点
            if(pstSpvnSlotMapGroup[j].bySeq < SPVN_SLOT_NUM)
            {   
                //从seq位置开始 遍历整个槽组，若seq所处位置为空，可不循环直接跳出
                for(i=0;i<SPVN_SLOT_NUM;i++)
                {
                    seq = pstSpvnSlotMapGroup[j].bySeq + i;
                    if(seq == SPVN_SLOT_NUM) //翻转溢出
                    {
                        seq = 0;
                    }
                    temp8 = (seq % SPVN_SLOT_SUB_GROUP_NUMBER) * SPVN_SLOT_SUB_GROUP_LENGTH + map[seq / SPVN_SLOT_SUB_GROUP_NUMBER];// 0 10 20 ..140 5 15 ... 145 5 15  ，又分成15个子组，每子组10个长度即20s；  
                    //该槽为空，或该nodeId已分配过位置
                    if(pstSpvnSlotMapGroup[j].byMallocedId[temp8] == 0 || nodeId == pstSpvnSlotMapGroup[j].byMallocedId[temp8])
                    {
                        pstSpvnSlotMapGroup[j].byMallocedId[temp8] =  nodeId; //malloced the nodeId
                        ret = temp8;
                        printf("SMC%d-%d-%d-%d",nodeId,j,temp8,seq);
                        setSpvnSlot(j,temp8,SPVN_TIME_DEFAULT); //debug 用
                        break;
                    } 
                }

                pstSpvnSlotMapGroup[j].bySeq = ++seq;//point to next
                if(ret >= 0)
                {
                    break;
                }
                else
                {
                    pstSpvnSlotMapGroup[j].bySeq = SPVN_SLOT_NUM; //遍历未找到空闲，故当前槽已满 
                }  
                            
            }// 

            if( pstSpvnSlotMapGroup[j].bySeq == SPVN_SLOT_NUM)//表明当前槽已满
            {
                printf("ESM%u-%u-%u",j,pstSpvnSlotMapGroup[0].bySeq,nodeId);
            }
        }
   
    }
    else
    {
        //printf("[ERROR] only %d s spvnTime support!\r\n",SPVN_TIME_DEFAULT);
    }
    
    return ret;
}

/***
 *@brief 节点删除或修改心跳时间后 先释放原有心跳时间槽 
 *@param nodeId，需释放
 *@return int >0 maclloced idx； -1 failed
 * 
*/
int spvnSlotFree(uint8_t nodeId)
{
    uint8_t i,j;
    uint8_t temp8;
    int ret = -1;
    const uint8_t deMap[SPVN_SLOT_SUB_GROUP_LENGTH] = {0,4,6,2,8,1,7,3,5,9};    
    for(j=0;j<SPVN_SLOT_GROUP_NUM;j++)
    {
        //遍历整个槽组
        for(i=0;i<SPVN_SLOT_NUM;i++)
        {
            if(pstSpvnSlotMapGroup[j].byMallocedId[i] ==  nodeId)//clear all the malloced id.
            {
                pstSpvnSlotMapGroup[j].byMallocedId[i] = 0;
                ret = i;
                temp8 = deMap[i % SPVN_SLOT_SUB_GROUP_LENGTH] * SPVN_SLOT_SUB_GROUP_NUMBER + i / SPVN_SLOT_SUB_GROUP_LENGTH;// 计算seq
                if(temp8 < pstSpvnSlotMapGroup[j].bySeq)
                {
                    pstSpvnSlotMapGroup[j].bySeq = temp8;      
                }

                printf("SFE%d-%d-%d-%d\r\n",nodeId,j,i,pstSpvnSlotMapGroup[j].bySeq);
            }
        } 
    }  

    return ret;   
} 

/***
 *@brief 节点删除或修改心跳时间后 先释放原有心跳时间槽  ( (u32)sysRtcCount >> 16)s % 150。则可算出当前所处的spvnSlotIdx
 *@param nodeId，表示分配给改Id的槽及未被分配的槽
 *@param slotGroupIdx，表示查找的组别
 *@param pMallocedIdxBitMask OUT出参 位域表示可用的心跳时间槽，该指针对象长度为 150bit/8 +1 = 19 bytes 
 *@param isIdleInclude 是否包含空闲的槽位
 *@return int >0 Gruop idx； -1 failed
 * 
*/
int spvnSlotAvailableGet(uint8_t nodeId, uint8_t slotGroupIdx, uint8_t *pMallocedIdxBitMask, uint8_t isIdleInclude)
{
    uint8_t i = 0;
    uint8_t k = 0;
    uint8_t m = 0;
    int ret = -1;
    if(slotGroupIdx >= SPVN_SLOT_GROUP_NUM)
    {
        //printf("[error] slotGroupIdx%u invalid\r\n",slotGroupIdx);
        return ret;
    }
    //遍历整个槽组
    for(i=0;i<SPVN_SLOT_NUM;i++)
    {
        if(0 == k)
        {
            if(pMallocedIdxBitMask)
                pMallocedIdxBitMask[m] = 0x00;
        }

        if(pstSpvnSlotMapGroup[slotGroupIdx].byMallocedId[i] == nodeId )//
        {
            if(pMallocedIdxBitMask)
                pMallocedIdxBitMask[m] |= (0x01<<k);
            ret =i;
            
        }
        else if (isIdleInclude && 0 == pstSpvnSlotMapGroup[slotGroupIdx].byMallocedId[i]) 
        {
            if(pMallocedIdxBitMask)
                pMallocedIdxBitMask[m] |= (0x01<<k);
        }


        k++;
        if(k == 8)
        {
            k = 0;
            m++;
        }
    } 
    
    return ret;   
} 


int checkChannelSlotAvailable(uint8_t destId,uint8_t transType)//避开后 开启定时器并应允许休眠
{
  int temp = 0;
  if(!isTimeSynced()) //若未时间同步 可处理该接口
  {
    return 0;
  }

  if(! CHECK_TRANS_EVENT_TYPE_IS_LBT_NOT_NEED(transType) ) //需要LBT的报文类型，即表示需要退避 广播窗口； 应答报文、广播、文件传输报文等不需要避开也不检测。
  {
      temp = READ_BU_RTC_COUNT() & 0xFFFF;
      if(65209 < temp)
      {
        return(65209 - temp); //避开主频切换窗口1
      }
      if(temp < 4260) //避开广播窗口1及主频切换窗口1，可告知上层等待时间
      {
        return (temp - 4260); //返回负数 表示距离下一个可用窗口的时间， ToDo， RxWake接收事件必须在规定接收窗口内 马上处理完成
      }

      if(12780 < temp && temp < 13434) //避开剩下的主频切换的4个窗口，ToDo，调节子设备RFM晶振负偏，则RFM发往HUB的报文需左偏
      {
         return(temp - 13434);
      }
      if(25887 < temp && temp < 26541) //避开剩下的主频切换的4个窗口
      {
         return(temp - 26541);
      }
      if(38994 < temp && temp < 39648) //避开剩下的主频切换的4个窗口
      {
         return(temp - 39648);
      }
      if(52102 < temp && temp < 52756) //避开剩下的主频切换的4个窗口
      {
         return(temp - 52756);
      }
  }

  return 0;
}

/**************************************************RFM使用，函数定义**************************************************************/
